package com.wanban.downloader.manager

import android.text.TextUtils
import android.util.Log
import com.blankj.utilcode.util.NetworkUtils
import com.blankj.utilcode.util.SPUtils
import com.wanban.downloader.MultiDownloadManager
import com.wanban.downloader.db.DownloadEntity
import com.wanban.downloader.listener.OnDownloadListener
import com.wanban.downloader.model.DownloadParams
import com.wanban.downloader.model.Status
import com.wanban.downloader.utils.Constants
import com.wanban.downloader.db.DatabaseUtils
import com.wanban.downloader.utils.AlertUtils
import com.wanban.downloader.utils.ErrorMessage
import com.wanban.downloader.utils.FileDownloadPathUtils
import com.wanban.downloader.utils.RxUtils


abstract class BaseDownloadManager(val listener: OnDownloadListener?) {

    companion object {
        const val TAG = "MultiDownload"
    }

    // 所有的下载队列，只不包括被删除的
    private val downloadQueue by lazy { ArrayList<DownloadEntity>() }
    /** 下载器的类型 */
    abstract fun getDownloadType(): Int

    /** 初始化，恢复下载队列内容，但是不开始下载 */
//    open fun init(application: Application, listener: OnDownloadListener?) {
//        downloadListener = listener
//        // 初始化，默认的保存地址是： /data/user/0/应用包名/files/mmkv
//        MMKV.initialize(application)
//        // 初始化Room数据库
//        DownloadRoomUtils.initRoom(application)
//        // 初始化数据库
//        DatabaseManager.initializeInstance(DownloadSQLiteHelper(application))
//        // 交换数据，废弃旧数据表
//        Log.e("NewTD", "准备初始化...")
//        val startTime = System.currentTimeMillis()
//        DatabaseUtils.exchangeDatabase { tasks ->
//            downloadQueue.clear()
//            isDataBaseInitSuccess = true
//            // 将重置状态后的列表加入当前下载栈
//            if (tasks.isNotEmpty()) {
//                downloadQueue.addAll(tasks)
//            }
//
//            val endTime = System.currentTimeMillis() - startTime
//            Log.e("NewTD", "初始化完成...耗时：$endTime ms")
//        }
//    }


    /**
     * 给当前下载器加入下载任务池，不做其他任何操作
     */
    fun addInitTasks(task: DownloadEntity){
        downloadQueue.add(task)
    }


    /**
     * 任务是否能启动的前置条件
     * @return true: 允许启动，反之。
     */

    private fun canResumeTasks(): Boolean {
        // 没有网络
        if (!NetworkUtils.isConnected()) return false
        // 只允许wifi下载且当前是流量
        if (NetworkUtils.isMobileData() && isOnlyWifiDownload()) return false
        // 没有任务，直接返回
        if (downloadQueue.isEmpty()) return false
        // 下载队列达到极限，无法进行新的下载
        if (!canRunningNewTask()) {
            Log.e(TAG, "下载队列达到极限，无法进行新的下载")
            return false
        }
        return true
    }

    /**
     * 尝试启动可以执行的任务，除了已完成，删除，暂停，出错超过最大尝试次数，这些状态的任务无法启动，剩余的都可以尝试启动
     */
    @Synchronized
    fun resumeAllDownload() {
        // 没有网络
        if (!canResumeTasks()) return
        // 找到可以尝试回复的下载，已经启动的或者暂停的不管，只负责尚未启动或者出错的
        var canStartList = downloadQueue.filter {
            it.state == Status.PENDING || (it.state == Status.ERROR && it.retryTime < DownloadParams.maxRetryTimes)
        }
        // 没有符合的条件
        if (canStartList.isEmpty()) return
        canStartList = ArrayList(canStartList.sortedBy { it.createTime })
        // 尝试在不超过最大下载个数的范围内，恢复下载
        val maxSize = DownloadParams.maxDownloadQueueCount.coerceAtMost(canStartList.size)
        for (index in 0 until maxSize) {
            val task = canStartList[index]
            // 启动任务
            startTaskAddedInStack(task)
        }
    }

    /** 下载真正的实现 */
    internal abstract fun executeStart(task: DownloadEntity)

    /**
     * 新增下载
     * 只负责新增的下载，如果不符合条件，不让加入
     */
    @Synchronized
    fun startDownload(task: DownloadEntity) {
        // 是否已经在任务队列中
        if (isAddedTask(task.url)) {
            AlertUtils.alert("任务已在下载队列中！")
            return
        }
        // 已达到最大任务栈
        if (!MultiDownloadManager.isAllowAddNewTask()) {
            AlertUtils.alert("已达到最大下载数量，请等待其它下载任务完成!")
            return
        }
        // ************* 不在任务栈中，判断是否能新增任务 *************
        startNewTaskInStack(task)
    }


    private fun startNewTaskInStack(task: DownloadEntity){
        // 先将文件路径确认
        val fileDirPath = FileDownloadPathUtils.getOrCreateVideoRootFileDirPath(task.getFileName())
        if (fileDirPath == null || TextUtils.isEmpty(fileDirPath)) {
            task.errorMsg = "文件根目录创建失败！"
            onErrorStatus(task, false)
            return
        }
        task.fileDirPath = fileDirPath
        task.createTime = System.currentTimeMillis()
        // 加入下载队列
        downloadQueue.add(task)
        // 先处于准备阶段，到开始下载之间的过程都是准备阶段
        onPendingStatus(task)
        // 继续检查下载是否满足条件
        if (!canTaskDownload(task)) {
            return
        }
        // 达到正在下载并行数量最大值，无法启动新下载
        val runningTaskCount = MultiDownloadManager.getRunningTaskCount()
        Log.e(TAG, "正在运行的任务数 -> $runningTaskCount")
        if (runningTaskCount >= DownloadParams.maxDownloadQueueCount) {
            return
        }
        onPrepareStatus(task)
        task.startTaskTime = System.currentTimeMillis()
        // 先解析对应的url
        executeStart(task)
    }


    /** 下载任务暂停的实现方法 */
    internal abstract fun stopDownloadTask(url: String)

    /**
     * 暂停下载
     */
    @Synchronized
    fun stopDownload(url: String, isAutoStartNextTask: Boolean = true) {
        // 暂停任务执行
        stopDownloadTask(url)
        // 更新任务状态，并尝试自动启动下一个任务
        onStopStatus(url, isAutoStartNextTask)
    }

    /**
     * 暂停所有下载
     */
    @Synchronized
    fun stopAllDownload(){
        // 获取正在执行的任务，并暂停他们
        getRunningTaskList().forEach {
            stopDownload(it.url, false)
        }
    }

    /**
     * 继续下载
     */
    @Synchronized
    fun resumeDownload(url: String) {
        val task = getTaskByUrl(url) ?: return
        startTaskAddedInStack(task)
    }

    // 启动已在任务栈中的任务
    @Synchronized
    private fun startTaskAddedInStack(task: DownloadEntity){
        // 重新从队列中获取任务，以队列中的任务为标准
        val currentTask = getTaskByUrl(task.url)?:return
        // 任务已经完成或删除，忽略
        if (currentTask.state == Status.SUCCESS || currentTask.state == Status.DELETE) return
        // 任务已经启动，忽略
        if (isTaskRunning(currentTask)) return
        // 继续检查下载是否满足条件
        if (!canTaskDownload(currentTask)) return
        // 尝试重新启动任务
        if (!canRunningNewTask()) { // 无法直接启动新任务
            // 寻找任务队列中执行最早的任务，将其暂停后，再执行新任务
            getRunningTaskList().minByOrNull { it.startTaskTime }?.let { oldTask ->
                // 只暂停当前正在下载的任务，不发送任务状态
                stopDownloadTask(oldTask.url)
                // 将任务处于PENDING状态
                onPendingStatus(oldTask)
            }
        }
        currentTask.isNetworkError = false
        // 重置重试状态，毕竟最终目的是为了下载
        currentTask.retryTime = 0
        // 启动时间
        currentTask.startTaskTime = System.currentTimeMillis()
        // 先处于准备阶段，到开始下载之间的过程都是准备阶段
        onPrepareStatus(currentTask)
        // 先解析对应的url
        executeStart(currentTask)
    }


    /**
     * 删除下载
     */
    @Synchronized
    fun deleteDownload(url: String, isAutoStartNextTask: Boolean = true) {
        // 执行自己的删除任务操作
        excSelfDeleteTask(url)
        // 如果任务未停止，先停止当前任务
        stopDownloadTask(url)
        // 队列删除此任务
        val task = getTaskByUrl(url)
        if (task != null) {
            downloadQueue.remove(task)
            // 异步删除文件
            deleteTaskFile(task)
            // 删除数据库记录
            DatabaseUtils.deleteTask(url) {
                // 发布消息更新状态
                task.state = Status.DELETE
                sendMessage(task)
                Log.e(TAG, "${task.videoName} => 视频已删除")
                // 启动下一个任务
                if (isAutoStartNextTask) {
                    Log.e(TAG, "准备启动下一个任务...")
                    readyToNextTask()
                }
            }
        } else {
            RxUtils.doObservable({
                val entity = DatabaseUtils.getTaskByUrl(url)
                if (entity == null) {
                    it.onComplete()
                    return@doObservable
                }
                // 异步删除文件
                deleteTaskFile(entity)
                // 删除数据库记录
                DatabaseUtils.deleteTaskSync(url)
                it.onNext(entity)
            },{ entity ->
                if (entity == null) return@doObservable
                // 删除数据库记录
                entity.state = Status.DELETE
                sendMessage(entity)
                // 启动下一个任务
                Log.e(TAG, "${entity.videoName} => 视频已删除")
                if (isAutoStartNextTask) {
                    Log.e(TAG, "准备启动下一个任务...")
                    readyToNextTask()
                }
            },{
                if (isAutoStartNextTask) {
                    Log.e(TAG, "准备启动下一个任务...")
                    readyToNextTask()
                }
            })
        }
    }

    /** 执行自己的下载逻辑 */
    internal open fun excSelfDeleteTask(url: String) {}

    /** 删除下载的任务文件 */
    internal abstract fun deleteTaskFile(task: DownloadEntity)

    protected open fun onPendingStatus(task: DownloadEntity) {
        task.state = Status.PENDING
        updateTaskAndSendMessage(task)
    }

    protected open fun onPrepareStatus(task: DownloadEntity) {
        task.state = Status.PREPARE
        updateTaskAndSendMessage(task)
    }

    protected open fun onStartStatus(url: String) {
        val task = getTaskByUrl(url) ?: return
        task.state = Status.START
        Log.d(TAG, "${task.getFileName()} => onStart...")
        updateTaskAndSendMessage(task)
    }

    protected open fun onProgressStatus(url: String, speed: Long, percent: Float, currentLength: Long) {
        val task = getTaskByUrl(url) ?: return
        // 屏蔽无效的状态
        if (!isTaskRunning(task)) return
//        val speedStr =  CommonUtils.getFormatSpeedString(speed)
//        val percentStr =  CommonUtils.getFormatPercentString(percent)
//        Log.d(TAG, "${task.getFileName()} => onProgress...$speedStr, $percentStr")
        task.speed = speed
        task.percent = percent
        task.contentLength = currentLength
        task.state = Status.DOWNLOADING
        updateTaskAndSendMessage(task)
    }

    protected open fun onStopStatus(url: String, isAutoStartNextTask: Boolean = true) {
        val task = getTaskByUrl(url) ?: return
        task.state = Status.STOP
        updateTaskAndSendMessage(task)
        if (isAutoStartNextTask) {
            readyToNextTask()
        }
    }

    protected open fun onErrorStatus(task: DownloadEntity?, isAutoStartNextTask: Boolean = true){
        if (task == null || task.state == Status.SUCCESS || task.state == Status.DELETE) return
        Log.e(TAG, "${task.getFileName()} => onError...${task.errorMsg}")
        // 暂停下载当前任务
        stopDownloadTask(task.url)
        // 标记当前出错状态
        task.state = Status.ERROR
        updateTaskAndSendMessage(task)
        // 不允许自动执行
        if (isAutoStartNextTask) {
            readyToNextTask()
        }
    }


    protected open fun onSuccessStatus(task: DownloadEntity) {
        task.state = Status.SUCCESS
        updateTaskAndSendMessage(task)
        readyToNextTask()
    }

    // 准备启动下一个任务
    fun readyToNextTask() {
        // 任务队列已满，忽略
        if (!canRunningNewTask()) return
        // 等待中的任务列表
        val nextTask = downloadQueue.firstOrNull { it.state == Status.PENDING }
        if (nextTask != null) {
            startTaskAddedInStack(nextTask)
        } else {
            var queueTotalCount = 0
            MultiDownloadManager.getDownloadManagers().forEach { manager ->
                queueTotalCount += manager.downloadQueue.count {
                    it.state == Status.PENDING
                }
            }
            if (queueTotalCount <= 0) return

            MultiDownloadManager.readyToNextTask(getDownloadType())
        }
    }

    /**
     * 设置是否只允许wifi下载
     */
    fun setOnlyWifiDownload(isOnlyWifi: Boolean) {
        SPUtils.getInstance().put(Constants.SP_KEY_IS_ONLY_WIFI_DOWNLOAD, isOnlyWifi, true)
    }

    /**
     * 是否只允许wifi下载
     */
    fun isOnlyWifiDownload(): Boolean {
        return SPUtils.getInstance().getBoolean(Constants.SP_KEY_IS_ONLY_WIFI_DOWNLOAD, true)
    }

    /**
     * 设置是否只在sdcard下载
     */
    fun setDownloadSdcard(isSdcard: Boolean) {
        SPUtils.getInstance().put(Constants.SP_KEY_DOWNLOAD_SDCARD, isSdcard, true)
    }
    /**
     * 是否只在sdcard下载
     */
    fun isDownloadSdcard(): Boolean {
        return SPUtils.getInstance().getBoolean(Constants.SP_KEY_DOWNLOAD_SDCARD, false)
    }

    /**
     * 更新状态并发送消息
     */
    internal fun updateTaskAndSendMessage(task: DownloadEntity) {
        DatabaseUtils.insertOrUpdateTask(task) {
            sendMessage(task)
        }
    }

    internal fun updateTask(task: DownloadEntity) {
        // 跟新db
        DatabaseUtils.insertOrUpdateTask(task) {}
    }

    private fun sendMessage(task: DownloadEntity) {
        listener?.onDownloadStatusChanged(task)
    }

    /**
     * 根据url，获取任务队列中的任务
     */
    fun getTaskByUrl(url: String): DownloadEntity? {
        return downloadQueue.firstOrNull { it.url == url }
    }

    /**
     * 返回当前任务栈中，可以正常下载的任务列表
     * */
    fun getCanRunningTasks(): List<DownloadEntity> {
        return downloadQueue.filter {
            it.state == Status.PENDING ||
                    it.state == Status.DOWNLOADING ||
                    it.state == Status.PREPARE ||
                    it.state == Status.START
        }
    }


    fun getDownloadingFragmentList(): List<DownloadEntity> {
        return downloadQueue.filter {
            it.state != Status.DELETE  && it.state != Status.SUCCESS
        }
    }



    /** 当前任务是否已在队列中 */
    private fun isAddedTask(url: String?): Boolean{
        return downloadQueue.any { it.url == url }
    }

    /** 获取当前队列中正在下载的任务 */
    fun getRunningTaskList(): List<DownloadEntity> {
        return downloadQueue.filter { it.state == Status.DOWNLOADING || it.state == Status.PREPARE || it.state == Status.START }
    }

    /** 是否能在启动新的下载任务 */
    private fun canRunningNewTask(): Boolean {
        val runningTaskCount = MultiDownloadManager.getRunningTaskCount()
        Log.e(TAG, "正在运行的任务数 -> $runningTaskCount")
        return runningTaskCount < DownloadParams.maxDownloadQueueCount
    }

    /** 检查task是否能正常执行的部分条件 */
    private fun canTaskDownload(task: DownloadEntity): Boolean {
        if (TextUtils.isEmpty(task.url) || TextUtils.isEmpty(task.videoName)) {
            Log.e(TAG, "url or videoName is null")
            task.errorMsg = "url or videoName is null!"
            onErrorStatus(task, isAutoStartNextTask = true)
            return false
        }
        // 没有网络
        if (!NetworkUtils.isConnected()) {
            Log.e(TAG, "${task.getFileName()} => 没有网络，直接返回错误...")
            task.isNetworkError = true
            task.errorMsg = ErrorMessage.ERROR_MSG_DISCONNECT_NET
            onPendingStatus(task)
            AlertUtils.alert("请检查您的网络链接！")
            return false
        }
        // 不允许使用流量下载
        if (NetworkUtils.isMobileData() && isOnlyWifiDownload()) {
            Log.e(TAG, "${task.getFileName()} => 当前不允许使用流量下载...")
            task.isNetworkError = true
            task.errorMsg = ErrorMessage.ERROR_MSG_ONLY_WIFI_DOWNLOAD
            onErrorStatus(task, isAutoStartNextTask = false)
            return false
        }
        return true
    }

    /** 任务是否正在执行 */
    fun isTaskRunning(task: DownloadEntity): Boolean{
        return task.state == Status.PREPARE || task.state == Status.DOWNLOADING || task.state == Status.START
    }

    fun isTaskRunning(url: String?): Boolean{
        if (TextUtils.isEmpty(url)) return false
        val task = getTaskByUrl(url!!)?:return false
        return task.state == Status.PREPARE || task.state == Status.DOWNLOADING || task.state == Status.START
    }



    /**
     * 网络回复或者出现变化
     * 优先判断是否是从无网络回复到有网络
     * 其次判断网络类型是否发生变化
     */
    fun resumeFromNetworkConnected(networkType: NetworkUtils.NetworkType){
        Log.e("NewNetwork", "onConnected...网络恢复")
        // 恢复网络下载只进行一次即可
        Log.e("NewNetwork", "onConnected...准备恢复下载")
        when (networkType){
            // wifi
            NetworkUtils.NetworkType.NETWORK_WIFI, NetworkUtils.NetworkType.NETWORK_ETHERNET -> {
                Log.e("NewNetwork", "onConnected...优先使用wifi...")
                // 先尝试恢复之前因为网络导致的下载失败
                RxUtils.delay(5000) {
                    Log.e("NewNetwork", "onConnected...能恢复下载的个数: " + downloadQueue.filter { it.isNetworkError }.size)
                    downloadQueue.filter { it.isNetworkError }.forEach { errorTask ->
                        errorTask.isNetworkError = false
                        // 当前正在下载的任务数量
                        resumeDownload(errorTask.url)
                    }
                }
            }
            // 移动流量
            NetworkUtils.NetworkType.NETWORK_5G, NetworkUtils.NetworkType.NETWORK_4G,
            NetworkUtils.NetworkType.NETWORK_3G, NetworkUtils.NetworkType.NETWORK_2G,
            NetworkUtils.NetworkType.NETWORK_UNKNOWN -> {
                Log.e("NewNetwork", "onConnected...如果恢复的是移动流量，需要检查是否允许流量下载...")
                if (isOnlyWifiDownload()) { // 如果只允许wifi下载，不在处理
                    Log.e("NewNetwork", "onConnected...只允许wifi下载，不在处理...")
                    getRunningTaskList().forEach {
                        it.isNetworkError = true
                        stopDownloadTask(it.url)
                        onPendingStatus(it)
                    }
                    return
                }
                Log.e("NewNetwork", "onConnected...准备恢复下载...")
                RxUtils.delay(5000) {
                    downloadQueue.filter { it.isNetworkError }.forEach { errorTask ->
                        errorTask.isNetworkError = false
                        // 当前正在下载的任务数量
                        resumeDownload(errorTask.url)
                    }
                }
            }
            // 无网络
            else -> {}
        }

    }
}